feat(netbird): enroll mitmproxy as a mesh peer - #107
Open
wiacekm wants to merge 137 commits into
Open
Conversation
Member
Blockers
Security (high)
Important
Cleanup
Test runIn a worktree at the PR head: bats 389 passed, 5 failed (2 pre-existing on master, 3 in |
Co-authored-by: Cursor <cursoragent@cursor.com>
… netbird container Co-authored-by: Cursor <cursoragent@cursor.com>
Add netbird.env as the single source of truth for version and tarball checksums, verify downloads in Dockerfile.wg-client before extract, inject build args at init via apply_netbird_build_args, and fix stale peer-remove comment in netbird.bash.
Read netbird_api_token and netbird_enrollment_key from user, project, and local settings layers (env overrides). Export NB_SETUP_KEY before docker compose in sandcat compose/run/attach/restart-proxy. Seed netbird_api_token on init --netbird and document settings-based configuration.
Enable cloud/existing/new NetBird management server flows in init and persist the selected management URL so runtime commands and compose wiring resolve it automatically. Add a self-hosted server template scaffold and focused tests/docs so self-hosted onboarding is explicit and repeatable.
Add `sandcat netbird server start|stop|status` as a thin wrapper around the provisioned self-hosted stack in ~/.config/sandcat/netbird-server. Improve API error handling for NetBird’s misleading 404-on-invalid-token responses, keep settings-sourced PATs in a local variable instead of exporting NB_API_TOKEN, and guard jq pretty-printing when the response is not valid JSON.
Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce CapabilityRuntimeError hierarchy with domain-specific exceptions for visibility, lease, and bundle version failures.
Introduce LeaseDecision and LeaseManager to issue time-bounded leases with quota decrement, exhaustion checks, and expiry evaluation for PoC 1 flows.
RevocationManager revokes by lease or ref with fail-closed catalog state. discover_capabilities returns metadata for Discoverable caps only.
Add CapabilityRuntime class that wires together catalog, leases, revocation, and observability. Implements all 7 protocol surfaces from spec §3.2: - check_current_capabilities: returns bundle with visible/leased tools - request_capability_lease: grants leases with PoC 1 params - revoke_capability: revokes by lease ID or capability ref - discover_capabilities: discovers by intent query - emit events: delegates to observability collector - record_action: decrements quota and revokes when exhausted - enforce_action: validates bundle version and visibility Includes integration test for full PoC 1 lifecycle (create_pr invisible → lease → visible → use → gone) plus unit tests for revoke, discovery, and version mismatch scenarios. All 23 tests pass. Co-authored-by: Cursor <cursoragent@cursor.com>
Thin check-then-act wrapper that re-fetches bundle version before each action and records leased tool usage. Co-authored-by: Cursor <cursoragent@cursor.com>
Wrap MCP-delivered tools via McpToolAdapter with write_note lifecycle (quota=3, ttl=5m) and capability-specific lease params in the runtime. Co-authored-by: Cursor <cursoragent@cursor.com>
Demonstrates the §5.1 lease lifecycle with AgentExecutionLoop and agent adaptation to draft_pr when create_pr is exhausted. Co-authored-by: Cursor <cursoragent@cursor.com>
…ackend protocol Co-authored-by: Cursor <cursoragent@cursor.com>
…mentations Co-authored-by: Cursor <cursoragent@cursor.com>
…etwork revoke Implements PhysicalRevocationBackend by delegating route and peer removal to an injectable NetBirdClient. Co-authored-by: Cursor <cursoragent@cursor.com>
Sandcat now only stores management URLs and keys. Drop --netbird-server, sandcat netbird server, and --proxy-peer; operators run those stacks from docs/examples/. Remove demo scripts and engineering-gate wrappers.
Missing-mount copy must not lead with re-init (that overwrites the project catalog). Default-no-restart must be asserted on a mounted project with a zero-call docker stub so compose cannot sneak through. Document that restart does not remount a new inode.
Capability-runtime is leaving this PoC; unknown-option is the operator-facing cut. Co-authored-by: Cursor <cursoragent@cursor.com>
The PoC no longer ships leases, catalog edit, or the Python control plane. Co-authored-by: Cursor <cursoragent@cursor.com>
Layer 1 remain the only in-proxy deny; mesh policy is the NetBird dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
Peer and ACL administration is the NetBird dashboard; compose still exports enroll env. Co-authored-by: Cursor <cursoragent@cursor.com>
Host exports literals or op:// pass:// paths; it never calls a secret provider CLI. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace and dns_label keep using a plaintext PAT after in-container lookup. Co-authored-by: Cursor <cursoragent@cursor.com>
Raw jq into NB_SETUP_KEY skipped object-shaped secrets before in-container resolve. Co-authored-by: Cursor <cursoragent@cursor.com>
The gateway is a compose PoC; tokens stay in a gitignored .env. Co-authored-by: Cursor <cursoragent@cursor.com>
Operator docs no longer describe capability leases or sandcat netbird. Co-authored-by: Cursor <cursoragent@cursor.com>
Mesh permission is dashboard ACL, not a NetBird route on wt0.
Hung op/pass-cli on mitmproxy startup could block gosu; raw jq dumped object-shaped tokens as garbage PATs. Timeouts, Proton warmup/retry, and flatten+resolve keep replace fail-closed without blocking mitmweb. Co-authored-by: Cursor <cursoragent@cursor.com>
…ded relay Match the combined server Relays list, coerce 0.72 ManagementURL objects, keep enrollment after replace failures, advertise STUN via docker0, and replace stale wg-client mesh DNS when peer IPs change. Co-authored-by: Cursor <cursoragent@cursor.com>
Setup-key-only first enroll was aborting because same-name lookup needs an API token. Skip replace when no token is present; still fail if a token is set and delete fails. Reject non-PAT pass-cli sessions before pass:// resolve. Drop the example lifecycle copy and build from the CLI template. Empty .env.example, cut the second CLI server walkthrough, and remove the accidental cli/.version.
wiacekm
force-pushed
the
sandcat-netbird-integration
branch
from
September 10, 2026 21:13
d26bb2d to
d194bbf
Compare
Route agent mounts and env into compose-agent.yml so Compose include no longer conflicts with compose-all.yml, keep NetBird BASE_IMAGE pins on SCT_MITMPROXY_VERSION, and fix bash-3 cache mapfile, installer PATH stubs, and the yq function PATH trap in secret-flatten tests.
Enrollment no longer blocks mitmweb; health sentinels and the CA publish run first. Peer names come from the project, enroll secrets stay in user settings, and mitmweb is no longer reachable on wt0 with a fixed password. Co-authored-by: Cursor <cursoragent@cursor.com>
Cache the mikefarah require check, skip stdout capture on in-place edits, and batch volume and capability writes into one yq -i each. Co-authored-by: Cursor <cursoragent@cursor.com>
wiacekm
force-pushed
the
sandcat-netbird-integration
branch
from
September 11, 2026 07:26
d194bbf to
1f045ac
Compare
Route agent mounts and env into compose-agent.yml so Compose include no longer conflicts with compose-all.yml, keep NetBird BASE_IMAGE pins on SCT_MITMPROXY_VERSION, and fix bash-3 cache mapfile, installer PATH stubs, and the yq function PATH trap in secret-flatten tests.
wiacekm
force-pushed
the
sandcat-netbird-integration
branch
from
September 11, 2026 11:28
1f045ac to
a667f1d
Compare
wiacekm
marked this pull request as ready for review
September 11, 2026 12:06
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Enroll mitmproxy as a NetBird mesh peer so a sandcat agent can reach overlay services without putting the NetBird client on wg-client (that path collided with
wg0).sandcat init --netbird(optional--netbird-management-url) turns mitmproxy into the mesh client: pinned NetBird0.72.4onwt0, project-scoped peer name{compose_project}-proxy, persisted/var/lib/netbirdstate, same-name replace when local state is gone, and adns_labelPATCH so the FQDN is stable. Agent traffic staysagent → wg0 → mitmproxy (Layer 1 + secrets) → internet or wt0. wg-client stays a tunnel shim; mesh DNS is published into the agent namespace via dnsmasq.Sandcat configures connection details only. It does not start a management server and there is no
sandcat netbirdhost CLI — peers and ACL live in the dashboard. Credentials (netbird_enrollment_key,netbird_api_token) may be literals orop:///pass://(or{value|op|pass}objects); the host flattens objects intoNB_*env and never shells out toop/pass-cli. Resolve happens inside mitmproxy, fail-closed on the replace path. Setup key and PAT stay in user settings; they are stripped from the copy bind-mounted into the agent.Operator examples (manual, not created by init):
docs/examples/netbird-server/— self-hosted combined 0.72 stackdocs/examples/proxy-peer/— compose+env HTTP gateway (GET /helloon 8080); Layer 1 allow + dashboard ACL (sandcat-proxy→proxy-peerTCP 8080); delete the DefaultAll↔Allpolicy; no NetBird Networks/legacy RoutesTest plan
batscomposefile NetBird + contract tests (singleNET_ADMINon wg-client in the template; mitmproxy getsNET_ADMINonly afterenable_netbird; checksum-pinned Dockerfile.mitmproxy)batsinit/devcontainer/regression:--netbirdwiring, peer-name seed/override, compose include vscompose-all.ymlbatsmitmproxy peer lifecycle + secret flatten (op:///pass://stubs, object flatten, PAT skip-replace)batsmitmproxy + wg-client NetBird DNSbatsnetbird-server start helper + proxy-peer compose contractsandcat init --netbird, confirm mitmproxy peer in the dashboard,netbird status --jsonshows0.72.4andrel://docs/examples/proxy-peer/with both env-files and--build; Layer 1 allow + ACL; from the agentGET http://<gateway-fqdn>:8080/hellocli/yqis not in the PR